#ifndef OAUTH_H
#define OAUTH_H

#include "GlobalConfig.h"
#include "Utils.h"
#include <wx/string.h>
#include <vector>

typedef std::vector< std::pair<wxString, wxString> > OAuthParamDataType;

class wxCriticalSection;

class OauthData
{
    public:
        OauthData();
        virtual ~OauthData();

#ifdef UNIT_TEST
        wxString consumer_key;
        wxString consumer_secret;
        wxString signature_method;
        wxString version;
#else
        const wxString consumer_key;
        const wxString consumer_secret;
        const wxString signature_method;
        const wxString version;
#endif

        wxString timestamp;
        wxString nonce;
        wxString signature;
        wxString verifier;
        wxString callback;

        wxString oauth_token;
        wxString oauth_secret;
        std::map<wxString, wxString> extraParams;

        enum OAuthStateType
        {
            OAUTH_REQUEST_TOKEN_PREPARE,
            OAUTH_ACCESS_TOKEN_PREPARE,
            OAUTH_ACCESS_TOKEN_DONE,
        };

        OAuthStateType mCurrentState;
        OAuthParamDataType packagedData;


        void reset()
        {
            timestamp.Clear();
            nonce.Clear();
            signature.Clear();
            verifier.Clear();
            callback.Clear();

            oauth_token.Clear();
            oauth_secret.Clear();

            packagedData.clear();
            extraParams.clear();
        }

        void buildParamData();
        //bool validateDataFormat(OAuthStateType state);

    private:
        wxCriticalSection*    mpBuildDataCS;
};

///-------------------------------------------------------------------------

class Oauth
{
    public:
        Oauth();
        virtual ~Oauth();

        static Oauth& GetInstance();

#ifdef UNIT_TEST
        friend class UnitTest_OAuth;
#endif
        friend class CLogin;

        const wxString& getBaseString(const wxString& baseURL, const wxString& httpMethod=HTTP_METHOD_GET, bool isUnique=true);
        const wxString& getOauthHeader();
        void signRequest(const wxString& baseStr);
        inline void resetData();
        inline void resetAccessPair();
        void setExtraQueryParams(const wxString& key, const wxString& val);
        void setExtraQueryParams(const std::map<wxString, wxString>& paramTable);

    private:
        OauthData*            mOauthData;
        wxString                 mBaseString;
        wxString                 mOauthHeader;
        std::pair<wxString , wxString> accessPair;  //save access token and secret for the current runtime session

        wxCriticalSection*    mpBaseStringCS;
        wxCriticalSection*    mpSignatureCS;
        wxCriticalSection*    mpHeaderCS;


        Oauth& operator=(const Oauth&); // avoid assginment when contains ptr member
        inline long getTimestamp() const;
        inline wxString getNonce() const;

        inline void setToken(const wxString& _token);   //only used in prepareForAccessToken
        inline void setSecret(const wxString& _token);  //only used in prepareForAccessToken
        inline void setVerifier(const wxString& _token);    //only used in prepareForAccessToken
        inline void enableCallback();   //only used in prepareForRequestToken
        inline bool setAccessPair(const wxString& _token, const wxString& _secret); //return false if any empty

        void initData();
        void destroyData();
};

inline void Oauth::resetData()
{
    initData();
}

inline long Oauth::getTimestamp() const
{
    return time(NULL);
}

inline wxString Oauth::getNonce() const
{
     srand(time(NULL));
     const wxString charSet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
     const size_t charSetLength = charSet.Length();
     const uint8_t fullLength = 8+rand()%16+rand()%8;
     wxString resultStr("");

     for(int a=0; a<fullLength; ++a)
     {
        resultStr.Append( charSet[rand()%charSetLength] );
     }

     return resultStr;
}

//rebuild to ensure correct packagedData, performance is the secondary goal here

inline void Oauth::setToken(const wxString& token)
{
    if(NULL != mOauthData)
    {
        mOauthData->oauth_token = token;
        mOauthData->buildParamData();
    }
}

inline void Oauth::setSecret(const wxString& secret)
{
    if(NULL != mOauthData)
    {
        mOauthData->oauth_secret = secret;
        mOauthData->buildParamData();
    }
}

inline void Oauth::setVerifier(const wxString& PIN)
{
    if(NULL != mOauthData)
    {
        mOauthData->verifier = PIN;
        mOauthData->buildParamData();
    }
}

inline bool Oauth::setAccessPair(const wxString& _token, const wxString& _secret)
{
    if(_token.IsEmpty() || _secret.IsEmpty())
        return false;

    accessPair.first = _token;
    accessPair.second = _secret;
    return true;
}

inline void Oauth::enableCallback()
{
    if(NULL != mOauthData)
    {
        mOauthData->callback = OAUTH_CALLBACK;
        mOauthData->buildParamData();
    }
}

inline void Oauth::resetAccessPair()
{
    accessPair.first.Clear();
    accessPair.second.Clear();
    if(NULL != mOauthData)
    {
        mOauthData->oauth_token.Clear();
        mOauthData->oauth_secret.Clear();
    }
}

#endif // OAUTH_H
